﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Net;
using System.Reflection;

namespace PINGWOL
{
    class Program
    {

        static string sIP;
        static string sMAC;
        static int iInterval;
        static int iWaitTime;

        static void Main(string[] args)
        {
            // Set default values
            sIP = "";// "192.168.1.6";
            sMAC = "";// "0:1A:A0:48:F1:9B";
            iInterval = 5;
            iWaitTime = 20;
           
            // Step through args         
            foreach (string arg in args)
            {
                if (arg.Length > 1)
                {
                    switch (arg.Substring(0,2).ToLower())
                    {
                        case "/u":
                            Console.Write("Please enter IP of device to monitor ? ");
                            sIP = Console.ReadLine();
                            Console.Write("Please enter MAC address of device to wake ? ");
                            sMAC = Console.ReadLine();
                            break;

                        case "/i":
                            sIP = arg.Substring(3);
                            Console.WriteLine("IP address set to " + sIP);
                            break;

                        case "/m":
                            sMAC = arg.Substring(3);
                            Console.WriteLine("MAC address set to " + sMAC);
                            break;

                        case "/t":
                            if (int.TryParse(arg.Substring(3), out iInterval))
                            {
                                Console.WriteLine("Time to wait between ping set to " + iInterval + " seconds");
                            }
                            else
                            {
                                Console.WriteLine("Parameter format not correct - \"" + arg + "\".");
                            }
                            break;

                        case "/w":
                            if (int.TryParse(arg.Substring(3), out iWaitTime))
                            {
                                Console.WriteLine("Wait time after magic packet set to " + iWaitTime + " minutes");
                            }
                            else
                            {
                                Console.WriteLine("Parameter format not correct - \"" + arg + "\".");
                            }
                            break;

                        default:
                            Console.WriteLine("Parameter format not correct - \"" + arg + "\".");
                            break;
                    }
                }
                else
                {
                    Console.WriteLine("Parameter format not correct - \"" + arg + "\".");                            
                }
            }
                                  
            // if ip and mac was not supplied display help
            if (sIP.Equals("") || sMAC.Equals(""))
            {
                Console.WriteLine("PINGWOL [[/I:xxx.xxx.xxx.xxx][/M:xx:xx:xx:xx:xx:xx]] [/T:sec] [/W:min] [/U]");
                Console.WriteLine();
                Console.WriteLine("  /I:xxx.xxx.xxx.xxx");
                Console.WriteLine("              IP address of device to ping.");
                Console.WriteLine("  /M:xx:xx:xx:xx:xx:xx");
                Console.WriteLine("              MAC address of device to wake on successful ping.");
                Console.WriteLine("  /T:sec      Time to wait between pings. [Default 3 secs]");
                Console.WriteLine("  /W:min      Wait time after wake packet has been sent. [Default 20 min]");
                Console.WriteLine("  /U          Prompt for IP and MAC address.");
                Console.WriteLine();
            }
            //start pinging
            else
            {
                Console.WriteLine("Monitoring started at " + DateTime.Now);
                Console.WriteLine();
                while (true)
                {
                    if (PingDevice(sIP))
                    {
                        Console.WriteLine("Response recieved from {0}",sIP);
                        WakeUp(GetMacArray(sMAC));
                        Console.WriteLine("Sent wake packet to {0}",sMAC);
                        Console.WriteLine("Pausing application for {0} minutes", iWaitTime);
                        Thread.Sleep(iWaitTime * 60000);
                        Console.WriteLine();
                    }
                    Thread.Sleep(iInterval * 1000);
                }
            }
        }

        private static bool PingDevice(string ip)
        {
            Ping pingSender = new Ping();            
            // Create a buffer of 32 bytes of data to be transmitted.
            byte[] packetData = Encoding.ASCII.GetBytes("................................");
            // Jump though 50 routing nodes tops, and don't fragment the packet
            PingOptions packetOptions = new PingOptions(50, true);
            // Send the ping
            PingReply pingReturn = pingSender.Send(ip, 3000, packetData, packetOptions);
            // Display ping results
            Console.CursorTop = Console.CursorTop - 1;
            Console.WriteLine(
                "Current response from {0} at {1} - {2}          ",
                sIP,
                DateTime.Now.ToLongTimeString(),
                pingReturn.Status.ToString());
            // Do something with the return
            if (pingReturn.Status == IPStatus.Success)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Sends a Wake-On-Lan packet to the specified MAC address.
        /// </summary>
        /// <param name="mac">Physical MAC address to send WOL packet to.</param>
        private static void WakeUp(byte[] mac)
        {
            // WOL packet is sent over UDP 255.255.255.0:40000.
            UdpClient client = new UdpClient();
            client.Connect(IPAddress.Broadcast, 40000);
            // WOL packet contains a 6-bytes trailer and 16 times a 6-bytes sequence containing the MAC address.
            byte[] packet = new byte[17 * 6];
            // Trailer of 6 times 0xFF.
            for (int i = 0; i < 6; i++)
                packet[i] = 0xFF;
            // Body of magic packet contains 16 times the MAC address.
            for (int i = 1; i <= 16; i++)
                for (int j = 0; j < 6; j++)
                    packet[i * 6 + j] = mac[j];
            // Submit WOL packet.
            client.Send(packet, packet.Length);
        }

        /// <summary>
        /// Converts an mac string to an byte array
        /// </summary>
        /// <param name="mac">The physical MAC address to convert. The string can have on of the fallowing formats.
        /// <list type="bullet">
        /// <item>
        /// <description>AA:BB:CC:DD:EE:FF</description>
        /// </item>
        /// <item>
        /// <description>AA-BB-CC-DD-EE-FF</description>
        /// </item>
        /// <item>
        /// <description>AABB.CCDD.EEFF</description>
        /// </item>
        /// <item>
        /// <description>AABBCCDDEEFF</description>
        /// </item>
        /// </list>
        /// </param>
        /// <exception cref="ArgumentNullException">thrown if <paramref name="mac"/> is null </exception>
        /// <exception cref="ArgumentException">thrown if <paramref name="mac"/> is not valid</exception>
        public static byte[] GetMacArray(string mac)
        {
            mac = mac.Replace("-", "");

            if (string.IsNullOrEmpty(mac)) throw new ArgumentNullException("mac");
            byte[] ret = new byte[6];
            try
            {
                string[] tmp = mac.Split(':', '-');
                if (tmp.Length != 6)
                {
                    tmp = mac.Split('.');
                    if (tmp.Length == 3)
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            ret[i * 2] = byte.Parse(tmp[i].Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
                            ret[i * 2 + 1] = byte.Parse(tmp[i].Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
                        }
                    }
                    else
                        for (int i = 0; i < 12; i += 2)
                            ret[i / 2] = byte.Parse(mac.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
                }
                else
                    for (int i = 0; i < 6; i++)
                        ret[i] = byte.Parse(tmp[i], System.Globalization.NumberStyles.HexNumber);
            }
            catch
            {
                throw new ArgumentException("Argument doesn't have the correct format: " + mac, "mac");
            }
            return ret;
        }

        private static string ApplicationVersion
        {
            get
            {
                Assembly objAssembly = Assembly.GetExecutingAssembly();
                Version AppVersion = objAssembly.GetName().Version;

                return "Version " + AppVersion.Major.ToString() + "." + AppVersion.Minor.ToString();
            }
        }

    }
}
